package org.dreamwork.tools.network.bridge.client.fx.controllers;

import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import org.dreamwork.config.KeyValuePair;
import org.dreamwork.network.service.ServiceFactory;
import org.dreamwork.tools.network.bridge.client.data.Server;
import org.dreamwork.tools.network.bridge.client.fx.DialogHelp;
import org.dreamwork.tools.network.bridge.client.services.IClientService;
import org.dreamwork.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;

/**
 * Created by seth.yang on 2020/3/9
 */
public class ServerManagerController extends AbstractController {
    @FXML private ListView<Server> servers;
    @FXML private Button btnRemove;
    @FXML private TextField txtName;
    @FXML private TextField txtHost;
    @FXML private Spinner<Integer> txtManagePort;
    @FXML private Spinner<Integer> txtTunnelPort;
    @FXML private Label txtErrorMessage;

    private IClientService service = ServiceFactory.get (IClientService.class);
    private boolean update = false;

    private final Logger logger = LoggerFactory.getLogger (ServerManagerController.class);

    @Override
    public void initialize (URL location, ResourceBundle resources) {
        txtManagePort.setValueFactory (new SpinnerValueFactory.IntegerSpinnerValueFactory (1, 65535, 50041));
        txtTunnelPort.setValueFactory (new SpinnerValueFactory.IntegerSpinnerValueFactory (1, 65535, 50042));

        servers.setOnMouseClicked (e->{
            Server server = servers.getSelectionModel ().getSelectedItem ();
            if (server != null) {
                fillData (server);
                update = true;

                btnRemove.setDisable (false);
            } else {
                btnRemove.setDisable (true);
            }
        });

        loadData ();
    }

    @Override
    public void setStage (Stage stage, String title) {
        this.stage = stage;
        if (stage != null) {
            URL icon = getClass ().getClassLoader ().getResource ("images/16x16/server.png");
            if (icon != null) try (InputStream in = icon.openStream ()) {
                stage.getIcons ().add (new Image (in));
            } catch (IOException ex) {
                logger.warn (ex.getMessage (), ex);
            }

            stage.setResizable (false);
            if (!StringUtil.isEmpty (title)) {
                stage.setTitle (title);
            }
        }
    }

    @FXML private void save () {
        String name = txtName.getText ();
        if (StringUtil.isEmpty (name)) {
            txtErrorMessage.setText (getResource ("err.server.name.missing"));
            txtName.requestFocus ();
            return;
        }

        Server server = service.get (name.trim ());
        if (server != null) {
            txtErrorMessage.setText (getResource ("err.server.name.exists", "name", name));
            txtName.requestFocus ();
            return;
        }

        String host = txtHost.getText ();
        if (StringUtil.isEmpty (host)) {
            txtErrorMessage.setText (getResource ("err.server.host.missing"));
            txtHost.requestFocus ();
            return;
        }

        int managePort = txtManagePort.getValue ();
        if (managePort <= 0 || managePort > 65535) {
            txtErrorMessage.setText (getResource (
                    "err.invalid.range",
                    new KeyValuePair<> ("name", getResource ("label.manage.port")),
                    new KeyValuePair<> ("min", 1),
                    new KeyValuePair<> ("max", 65535)
            ));
            txtManagePort.requestFocus ();
            return;
        }

        server = service.get (host, managePort);
        if (server != null) {
            txtErrorMessage.setText (getResource (
                    "err.server.host.exists",
                    new KeyValuePair<> ("host", host),
                    new KeyValuePair<> ("port", managePort)
            ));
            txtHost.requestFocus ();
            return;
        }

        int connectPort = txtTunnelPort.getValue ();
        if (connectPort <= 0 || connectPort > 65535) {
            txtErrorMessage.setText (getResource (
                    "err.invalid.range",
                    new KeyValuePair<> ("name", getResource ("label.connector.port")),
                    new KeyValuePair<> ("min", 1),
                    new KeyValuePair<> ("max", 65535)
            ));
            txtManagePort.requestFocus ();
            return;
        }

        background (()->{
            Server remote = new Server ();
            remote.setConnectorPort (connectPort);
            remote.setManagePort (managePort);
            remote.setHost (host);
            remote.setName (name);
            if (update) {
                service.update (remote);
            } else {
                service.save (remote);
            }
        });

        loadData ();
    }

    @FXML private void create () {
        reset ();
        update = false;
        txtName.requestFocus ();
    }

    @FXML private void remove () {
        Server server = servers.getSelectionModel ().getSelectedItem ();
        if (server == null) {
            return;
        }

        DialogHelp.prompt (getResource ("prompt.confirm.remove")).ifPresent (type -> {
            if (type == ButtonType.OK) {
                if (service.isServerInUse (server.getName ())) {
                    DialogHelp.error (getResource ("err.server.in-use", "name", server.getName ()));
                } else {
                    service.delete (server);
                    reset ();
                    loadData ();
                }
            }
        });
    }

    private void loadData () {
        background (()->{
            List<Server> list = service.getServers ();
            post (() -> {
                servers.getItems ().clear ();
                servers.getItems ().addAll (list);
            });
        });
    }

    private void fillData (Server server) {
        if (server != null) {
            txtName.setText (server.getName ());
            txtHost.setText (server.getHost ());
            txtErrorMessage.setText ("");
            txtTunnelPort.getValueFactory ().setValue (server.getConnectorPort ());
            txtManagePort.getValueFactory ().setValue (server.getManagePort ());
            update = true;
        }
    }

    private void reset () {
        txtHost.setText ("");
        txtName.setText ("");
        txtErrorMessage.setText ("");
        txtManagePort.getValueFactory ().setValue (50041);
        txtTunnelPort.getValueFactory ().setValue (50042);
    }
}
